home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Linux Cubed Series 7: Sunsite
/
Linux Cubed Series 7 - Sunsite Vol 1.iso
/
system
/
linux-bo
/
etherboo.000
/
etherboo
/
etherboot-2.0
/
netboot-freebsd
/
main.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-06-18
|
19KB
|
661 lines
/**************************************************************************
NETBOOT - BOOTP/TFTP Bootstrap Program
Author: Martin Renters
Date: Dec/93
**************************************************************************/
/* #define MDEBUG */
#include "netboot.h"
int jmp_bootmenu[10];
struct exec head;
char *loadpoint;
char *kernel;
char kernel_buf[128];
void (*kernelentry)();
struct nfs_diskless nfsdiskless;
int hostnamelen;
char config_buffer[TFTP_MAX_PACKET+1]; /* +1 for null byte */
struct bootinfo bootinfo;
unsigned long netmask;
char broadcast[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
/**************************************************************************
MAIN - Kick off routine
**************************************************************************/
#ifdef __linux
extern int main P((void));
int _main() {return main();}
#endif
int main()
{
extern char *linux_add_cmdline();
char *p;
for (p=_edata; p<_end; p++) *p = 0; /* Zero BSS */
#ifdef ASK_BOOT
while (1) {
int c;
printf("\n\rBoot from Network (Y/N) ? ");
c = getchar();
if ((c >= 'a') && (c <= 'z')) c &= 0x5F;
if (c == '\r') break;
putchar(c);
if (c == 'N')
exit(0);
if (c == 'Y')
break;
printf(" - bad response\n\r");
}
#endif
gateA20();
#ifdef NETBOOT32
printf("\r\nBOOTP/TFTP/NFS bootstrap loader " VERSION " ESC for menu\r\n\r\nSearching for adapter...");
#endif
#ifdef NETBOOT16
/* bcc still doesn't do ANSI string concatenation so this is hardwired */
printf("\r\nBOOTP/TFTP/NFS bootstrap loader 2.0 ESC for menu\r\n\r\nSearching for adapter...");
#endif
if (!eth_probe()) {
printf("No adapter found.\r\n");
exit(0);
}
kernel = DEFAULT_BOOTFILE;
linux_add_cmdline(0); /* clear linux cmdline */
while (1) {
if (setjmp(jmp_bootmenu))
bootmenu();
else
load();
}
}
#ifdef NETBOOT32
/**************************************************************************
DOMOUNT - Try to mount FS
**************************************************************************/
void domount(char *s,int *port,int *mount_port,char *hostnam,char *fh,int arpserver,
struct sockaddr_in *in_saddr,struct nfs_args *args,char *fnam,
char *newfh)
{
int err;
*port = rpclookup(arpserver, PROG_NFS, 2);
*mount_port = rpclookup(arpserver, PROG_MOUNT, 1);
if ((*port == -1) || (*mount_port == -1)) {
printf("Unable to get %s NFS/MOUNT ports\r\n",s);
bootmenu:
longjmp(jmp_bootmenu,1); }
if ((err = nfs_mount(arpserver, *mount_port, hostnam, fh))) {
printf("Unable to mount %s filesystem: ",s);
nfserr:
nfs_err(err);
goto bootmenu; }
in_saddr->sin_len = sizeof(struct sockaddr_in);
in_saddr->sin_family = AF_INET;
in_saddr->sin_port = htons(*port);
in_saddr->sin_addr.s_addr = htonl(arptable[arpserver].ipaddr);
args->timeo = 10;
args->retrans = 100;
if ((err = nfs_lookup(arpserver,*port,fh,fnam,newfh))) {
printf("Unable to open %s: ",fnam);
goto nfserr; }
return;
}
#endif /* NETBOOT32 */
/**************************************************************************
LOAD - Try to get booted
**************************************************************************/
void load()
{
char *p,*q;
char cfg[64];
int root_nfs_port;
int root_mount_port;
int swap_nfs_port;
int swap_mount_port;
char kernel_handle[32];
char cmd_line[80];
int err, offset, read_size;
long addr, broadcast;
/* Initialize this early on */
nfsdiskless.root_args.rsize = 8192;
nfsdiskless.root_args.wsize = 8192;
nfsdiskless.swap_args.rsize = 8192;
nfsdiskless.swap_args.wsize = 8192;
nfsdiskless.root_args.sotype = SOCK_DGRAM;
nfsdiskless.root_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE);
nfsdiskless.swap_args.sotype = SOCK_DGRAM;
nfsdiskless.swap_args.flags = (NFSMNT_WSIZE | NFSMNT_RSIZE);
/* Find a server to get BOOTP reply from */
if (!arptable[ARP_CLIENT].ipaddr || !arptable[ARP_SERVER].ipaddr) {
printf("\r\nSearching for server...\r\n");
if (!bootp()) {
printf("No Server found.\r\n");
bootmenu:
longjmp(jmp_bootmenu,1);
}
}
printf("My IP %I, Server IP %I, GW IP %I\r\n",
arptable[ARP_CLIENT].ipaddr,
arptable[ARP_SERVER].ipaddr,
arptable[ARP_GATEWAY].ipaddr);
#ifdef MDEBUG
printf("\n=>>"); getchar();
#endif
/* Now use TFTP to load configuration file */
#if defined(PRIORIZEBOOTPKERNEL) && !defined(BOOTPKERNELONLY)
printf(" \r\nLoading %s... ",kernel);
if (!tftp(kernel)) {
#endif
#ifndef BOOTPKERNELONLY
sprintf(cfg,"cfg.%I",arptable[ARP_CLIENT].ipaddr);
printf(" \r\nLoading %s... ",cfg);
if (!tftp(cfg)) {
sprintf(cfg,"/tftpboot/cfg.%I",arptable[ARP_CLIENT].ipaddr);
printf(" \r\nLoading %s... ",cfg);
if (!tftp(cfg)) {
#endif
#ifndef PRIORIZEBOOTPKERNEL
printf(" \r\nLoading %s... ",kernel);
if (!tftp(kernel)) {
#endif
printf("Unable to load config file.\r\n");
goto bootmenu;
#if !defined(PRIORIZEBOOTPKERNEL) || \
defined(PRIORIZEBOOTPKERNEL) && !defined(BOOTPKERNELONLY)
}
#endif
#ifndef BOOTPKERNELONLY
} }
#endif
#ifdef NETBOOT32
#ifdef MDEBUG
printf("\n=>>"); getchar();
#endif
p = config_buffer;
while(*p) {
q = cmd_line;
while ((*p != '\n') && (*p)) *(q++) = *(p++);
*q = 0;
printf("%s\r\n",cmd_line);
execute(cmd_line);
if (*p) p++;
}
#ifdef MDEBUG
printf("\n=>>"); getchar();
#endif
/* Check to make sure we've got a rootfs */
if (!arptable[ARP_ROOTSERVER].ipaddr) {
printf("No ROOT filesystem server!\r\n");
goto bootmenu;
}
/* Fill in nfsdiskless.myif */
sprintf(&nfsdiskless.myif.ifra_name,eth_driver);
nfsdiskless.myif.ifra_addr.sa_len = sizeof(struct sockaddr);
nfsdiskless.myif.ifra_addr.sa_family = AF_INET;
addr = htonl(arptable[ARP_CLIENT].ipaddr);
bcopy(&addr, &nfsdiskless.myif.ifra_addr.sa_data[2], 4);
broadcast = (addr & netmask) | ~netmask;
nfsdiskless.myif.ifra_broadaddr.sa_len = sizeof(struct sockaddr);
nfsdiskless.myif.ifra_broadaddr.sa_family = AF_INET;
bcopy(&broadcast, &nfsdiskless.myif.ifra_broadaddr.sa_data[2], 4);
addr = htonl(arptable[ARP_GATEWAY].ipaddr);
if (addr) {
nfsdiskless.mygateway.sin_len = sizeof(struct sockaddr);
nfsdiskless.mygateway.sin_family = AF_INET;
bcopy(&addr, &nfsdiskless.mygateway.sin_addr, 4);
} else {
nfsdiskless.mygateway.sin_len = 0;
}
nfsdiskless.myif.ifra_mask.sa_len = sizeof(struct sockaddr);
nfsdiskless.myif.ifra_mask.sa_family = AF_UNSPEC;
bcopy(&netmask, &nfsdiskless.myif.ifra_mask.sa_data[2], 4);
rpc_id = currticks();
/* Lookup NFS/MOUNTD ports for SWAP using PORTMAP */
if (arptable[ARP_SWAPSERVER].ipaddr) {
char swapfs_fh[32], swapfile[32];
sprintf(swapfile,"swap.%I",arptable[ARP_CLIENT].ipaddr);
domount("SWAP",&swap_nfs_port,&swap_mount_port,nfsdiskless.swap_hostnam,
swapfs_fh,ARP_SWAPSERVER,&nfsdiskless.swap_saddr,
&nfsdiskless.swap_args,swapfile,nfsdiskless.swap_fh); }
/* Lookup NFS/MOUNTD ports for ROOT using PORTMAP */
domount("ROOT",&root_nfs_port,&root_mount_port,nfsdiskless.root_hostnam,
nfsdiskless.root_fh,ARP_ROOTSERVER,&nfsdiskless.root_saddr,
&nfsdiskless.root_args,*kernel == '/' ? kernel+1 : kernel,
kernel_handle);
nfsdiskless.root_time = 0;
/* Load the kernel using NFS */
printf("Loading %s...\r\n",kernel);
if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port, (char *)&kernel_handle, 0,
sizeof(struct exec), (char *)&head)) < 0) {
printf("Unable to read %s: ",kernel);
nfs_err(err);
goto bootmenu;
}
if (N_BADMAG(head) &&
!load_linux(root_mount_port,swap_mount_port,
root_nfs_port,kernel_handle)) {
printf("Bad executable format!\r\n");
goto bootmenu;
}
loadpoint = (char *)0x100000;
offset = N_TXTOFF(head);
/* printf("text=0x%X, ",head.a_text); */
while (head.a_text > 0) {
read_size = head.a_text > NFS_READ_SIZE ?
NFS_READ_SIZE : head.a_text;
if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port,
(char *)&kernel_handle, offset, read_size, loadpoint)) !=
read_size) {
if (err < 0) {
/* printf("Unable to read text: "); */
nfs_err(err);
}
goto bootmenu;
}
loadpoint += err;
head.a_text -= err;
offset += err;
}
while (((int)loadpoint) & CLOFSET)
*(loadpoint++) = 0;
/* printf("data=0x%X, ",head.a_data); */
while (head.a_data > 0) {
read_size = head.a_data > NFS_READ_SIZE ?
NFS_READ_SIZE : head.a_data;
if ((err = nfs_read(ARP_ROOTSERVER, root_nfs_port,
(char *)&kernel_handle, offset, read_size, loadpoint)) !=
read_size) {
if (err < 0) {
/* printf("Unable to read data: "); */
nfs_err(err);
}
goto bootmenu;
}
loadpoint += err;
head.a_data -= err;
offset += err;
}
/* printf("bss=0x%X, ",head.a_bss); */
while(head.a_bss--) *(loadpoint++) = 0;
/* printf("entry=0x%X.\n\r",head.a_entry); */
/* Jump to kernel */
bootinfo.bi_version = BOOTINFO_VERSION;
bootinfo.bi_kernelname = kernel;
bootinfo.bi_nfs_diskless = &nfsdiskless;
kernelentry = (void *)(head.a_entry & 0x00FFFFFF);
(*kernelentry)(0,NODEV,0,0,0,&bootinfo,0,0,0);
/* printf("*** %s execute failure ***\n",kernel); */
#endif /* NETBOOT32 */
}
/**************************************************************************
POLLKBD - Check for Interrupt from keyboard
**************************************************************************/
void pollkbd()
{
if (iskey() && (getchar() == ESC)) longjmp(jmp_bootmenu,1);
}
/**************************************************************************
DEFAULT_NETMASK - Set a default netmask for IP address
**************************************************************************/
void default_netmask()
{
int net = arptable[ARP_CLIENT].ipaddr >> 24;
if (net <= 127)
netmask = htonl(0xff000000);
else if (net < 192)
netmask = htonl(0xffff0000);
else
netmask = htonl(0xffffff00);
}
/**************************************************************************
UDP_TRANSMIT - Send a UDP datagram
**************************************************************************/
int udp_transmit(destip, srcsock, destsock, len, buf)
unsigned long destip;
unsigned int srcsock, destsock;
int len;
char *buf;
{
struct iphdr *ip;
struct udphdr *udp;
struct arprequest arpreq;
int arpentry, i;
int retry = MAX_ARP_RETRIES;
ip = (struct iphdr *)buf;
udp = (struct udphdr *)(buf + sizeof(struct iphdr));
ip->verhdrlen = 0x45;
ip->service = 0;
ip->len = htons(len);
ip->ident = 0;
ip->frags = 0;
ip->ttl = 60;
ip->protocol = IP_UDP;
ip->chksum = 0;
convert_ipaddr(ip->src, (char *)&arptable[ARP_CLIENT].ipaddr);
convert_ipaddr(ip->dest, (char *)&destip);
ip->chksum = ipchksum((unsigned short *)buf, sizeof(struct iphdr));
udp->src = htons(srcsock);
udp->dest = htons(destsock);
udp->len = htons(len - sizeof(struct iphdr));
udp->chksum = 0;
if (destip == IP_BROADCAST) {
eth_transmit(broadcast, IP, len, buf);
} else {
long h_netmask = ntohl(netmask);
/* Check to see if we need gateway */
if (((destip & h_netmask) !=
(arptable[ARP_CLIENT].ipaddr & h_netmask)) &&
arptable[ARP_GATEWAY].ipaddr)
destip = arptable[ARP_GATEWAY].ipaddr;
for(arpentry = 0; arpentry<MAX_ARP; arpentry++)
if (arptable[arpentry].ipaddr == destip) break;
if (arpentry == MAX_ARP) {
printf("\r\n%I is not in my arp table!\r\n", destip);
return(0);
}
for (i = 0; i<ETHER_ADDR_SIZE; i++)
if (arptable[arpentry].node[i]) break;
if (i == ETHER_ADDR_SIZE) { /* Need to do arp request */
arpreq.hwtype = htons(1);
arpreq.protocol = htons(IP);
arpreq.hwlen = ETHER_ADDR_SIZE;
arpreq.protolen = 4;
arpreq.opcode = htons(ARP_REQUEST);
bcopy(arptable[ARP_CLIENT].node, arpreq.shwaddr,
ETHER_ADDR_SIZE);
convert_ipaddr(arpreq.sipaddr,
(char *)&arptable[ARP_CLIENT].ipaddr);
bzero(arpreq.thwaddr, ETHER_ADDR_SIZE);
convert_ipaddr(arpreq.tipaddr, (char *)&destip);
while (retry--) {
eth_transmit(broadcast, ARP, sizeof(arpreq),
(char *)&arpreq);
if (await_reply(AWAIT_ARP, arpentry,
arpreq.tipaddr)) goto xmit;
}
return(0);
}
xmit: eth_transmit(arptable[arpentry].node, IP, len, buf);
}
return(1);
}
/**************************************************************************
TFTP - Try to load configuation file
**************************************************************************/
int tftp(name)
char *name;
{
int retry = MAX_TFTP_RETRIES;
static unsigned short isocket = 2000;
unsigned short osocket;
unsigned short len, block = 1, prevblock = 0;
struct tftp_t *tr;
struct tftp_t tp;
tp.opcode = htons(TFTP_RRQ);
len = (sprintf((char *)tp.u.rrq, "%s%coctet", name, 0)
- ((char *)&tp)) + 1;
if (!udp_transmit(arptable[ARP_SERVER].ipaddr, ++isocket, TFTP,
len, (char *)&tp))
return (0);
for (;;)
{
if (!await_reply(AWAIT_TFTP, isocket, NULL))
{
if (prevblock == 0 && retry-- > 0)
{ /* maybe initial request was lost */
if (!udp_transmit(arptable[ARP_SERVER].ipaddr,
++isocket, TFTP, len, (char *)&tp))
return (0);
continue;
}
break; /* timeout on other blocks */
}
tr = (struct tftp_t *)&packet[ETHER_HDR_SIZE];
if (tr->opcode == ntohs(TFTP_ERROR))
{
printf("TFTP error %d (%s)\r\n",
ntohs(tr->u.err.errcode),
tr->u.err.errmsg);
break;
}
if (tr->opcode != ntohs(TFTP_DATA))
break;
tp.opcode = htons(TFTP_ACK);
block = ntohs(tp.u.ack.block = tr->u.data.block);
osocket = ntohs(tr->udp.src);
len = ntohs(tr->udp.len) - sizeof(struct udphdr) - 4;
if (len > TFTP_MAX_PACKET) /* shouldn't happen */
continue; /* ignore it */
udp_transmit(arptable[ARP_SERVER].ipaddr, isocket,
osocket, TFTP_MIN_PACKET, (char *)&tp); /* ack */
if (block <= prevblock) /* retransmission */
continue; /* don't process */
prevblock = block;
if (block == 1)
{
if (len == TFTP_MAX_PACKET &&
(((unsigned short *)tr->u.data.download)[255] == 0xAA55 ||
*((unsigned long *)tr->u.data.download) == 0x1B031336l))
printf("Tagged file format");
else
{
bcopy(tr->u.data.download, config_buffer, len);
config_buffer[len] = 0;
return (1);
}
}
if (!linux_tftp(block, tr->u.data.download, len))
break;
if (len < TFTP_MAX_PACKET) /* End of data */
return (1);
}
return (0);
}
/**************************************************************************
BOOTP - Get my IP address and load information
**************************************************************************/
int bootp()
{
int retry = MAX_BOOTP_RETRIES;
struct bootp_t bp;
unsigned long starttime;
bzero(&bp, sizeof(struct bootp_t));
bp.bp_op = BOOTP_REQUEST;
bp.bp_htype = 1;
bp.bp_hlen = ETHER_ADDR_SIZE;
bp.bp_xid = starttime = currticks();
bcopy(arptable[ARP_CLIENT].node, bp.bp_hwaddr, ETHER_ADDR_SIZE);
while(retry--) {
udp_transmit(IP_BROADCAST, 0, BOOTP_SERVER,
sizeof(struct bootp_t), (char *)&bp);
if (await_reply(AWAIT_BOOTP, 0, NULL)) {
bcopy(&packet[ETHER_HDR_SIZE],(char *)&bootp_reply,sizeof(bootp_reply));
return(1); }
bp.bp_secs = htons((currticks()-starttime)/20);
}
return(0);
}
/**************************************************************************
AWAIT_REPLY - Wait until we get a response for our request
**************************************************************************/
int await_reply(type, ival, ptr)
int type, ival;
char *ptr;
{
unsigned long time;
struct iphdr *ip;
struct udphdr *udp;
struct arprequest *arpreply;
struct bootp_t *bootpreply;
struct rpc_t *rpc;
int protohdrlen = ETHER_HDR_SIZE + sizeof(struct iphdr) +
sizeof(struct udphdr);
time = currticks() + TIMEOUT;
while(time > currticks()) {
pollkbd();
if (eth_poll()) { /* We have something! */
/* Check for ARP - No IP hdr */
if ((type == AWAIT_ARP) &&
(packetlen >= ETHER_HDR_SIZE +
sizeof(struct arprequest)) &&
(((packet[12] << 8) | packet[13]) == ARP)) {
arpreply = (struct arprequest *)
&packet[ETHER_HDR_SIZE];
if ((arpreply->opcode == ntohs(ARP_REPLY)) &&
bcompare(arpreply->sipaddr, ptr, 4)) {
bcopy(arpreply->shwaddr,
arptable[ival].node,
ETHER_ADDR_SIZE);
return(1);
}
continue;
}
/* Anything else has IP header */
if ((packetlen < protohdrlen) ||
(((packet[12] << 8) | packet[13]) != IP)) continue;
ip = (struct iphdr *)&packet[ETHER_HDR_SIZE];
if ((ip->verhdrlen != 0x45) ||
ipchksum((unsigned short *)ip, sizeof(struct iphdr)) ||
(ip->protocol != IP_UDP)) continue;
udp = (struct udphdr *)&packet[ETHER_HDR_SIZE +
sizeof(struct iphdr)];
/* BOOTP ? */
bootpreply = (struct bootp_t *)&packet[ETHER_HDR_SIZE];
if ((type == AWAIT_BOOTP) &&
(packetlen >= (ETHER_HDR_SIZE +
sizeof(struct bootp_t))) &&
(ntohs(udp->dest) == BOOTP_CLIENT) &&
(bootpreply->bp_op == BOOTP_REPLY)) {
convert_ipaddr((char *)&arptable[ARP_CLIENT].ipaddr,
bootpreply->bp_yiaddr);
default_netmask();
convert_ipaddr((char *)&arptable[ARP_SERVER].ipaddr,
bootpreply->bp_siaddr);
bzero(arptable[ARP_SERVER].node,
ETHER_ADDR_SIZE); /* Kill arp */
convert_ipaddr((char *)&arptable[ARP_GATEWAY].ipaddr,
bootpreply->bp_giaddr);
bzero(arptable[ARP_GATEWAY].node,
ETHER_ADDR_SIZE); /* Kill arp */
if (bootpreply->bp_file[0]) {
bcopy(bootpreply->bp_file,
kernel_buf, 128);
kernel = kernel_buf;
}
decode_rfc1048(bootpreply->bp_vend);
return(1);
}
/* TFTP ? */
if ((type == AWAIT_TFTP) &&
(ntohs(udp->dest) == ival)) return(1);
/* RPC */
rpc = (struct rpc_t *)&packet[ETHER_HDR_SIZE];
if ((type == AWAIT_RPC) &&
(ntohs(udp->dest) == RPC_SOCKET) &&
(ntohl(rpc->u.reply.id) == ival) &&
(ntohl(rpc->u.reply.type) == MSG_REPLY)) {
#ifdef NETBOOT32
rpc_id++;
#endif /* NETBOOT32 */
return(1);
}
}
}
return(0);
}
/**************************************************************************
DECODE_RFC1048 - Decodes RFC1048 header
**************************************************************************/
void decode_rfc1048(p)
unsigned char *p;
{
static char rfc1048_cookie[4] = RFC1048_COOKIE;
unsigned char *end = p + BOOTP_VENDOR_LEN,*q;
if (bcompare(p, rfc1048_cookie, 4)) { /* RFC 1048 header */
p += 4;
while(p < end) {
unsigned char c = *p;
if (c == RFC1048_PAD) {p++; continue;}
else if (c == RFC1048_END) {p = end; continue; }
else if (c == RFC1048_NETMASK) {bcopy(p+2,&netmask,4); }
else if (c == RFC1048_HOSTNAME) {
bcopy(p+2,&nfsdiskless.my_hostnam,TAG_LEN(p));
hostnamelen = (TAG_LEN(p)+3)&~3; }
else {
printf("Unknown RFC1048-tag ");
for(q=p;q<p+2+TAG_LEN(p);q++)
printf("%x ",*q);
printf("\n\r"); }
p += TAG_LEN(p) + 2;
}
}
}
/**************************************************************************
IPCHKSUM - Checksum IP Header
**************************************************************************/
unsigned short ipchksum(ip, len)
unsigned short *ip;
int len;
{
unsigned long sum = 0;
len >>= 1;
while (len--) {
sum += *(ip++);
if (sum > 0xFFFF)
sum -= 0xFFFF;
}
return((~sum) & 0x0000FFFF);
}
/**************************************************************************
CONVERT_IPADDR - Convert IP address from net to machine order
**************************************************************************/
void convert_ipaddr(d, s)
char *d,*s;
{
*(d+3) = *s;
*(d+2) = *(s+1);
*(d+1) = *(s+2);
*d = *(s+3);
}